package de.codi.read;

import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.mindswap.pellet.utils.Namespaces;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAnnotation;
import org.semanticweb.owlapi.model.OWLAnnotationAssertionAxiom;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLDataProperty;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.reasoner.OWLReasoner;

import com.clarkparsia.pellet.owlapiv3.PelletReasonerFactory;

import de.codi.exception.CodiException;
import de.codi.string.StringPreparation;


public class StoredOntology
{

    public OWLOntologyManager          man;
    public OWLDataFactory              factory;
    public OWLOntology                 ont;

    public OWLReasoner                 reasoner         = null;

    public OWLClass                    THING            = null;
    public OWLClass                    NOTHING          = null;

    public HashSet<OWLClass>           classes          = null;
    public HashSet<OWLObjectProperty>  objectProperties = null;
    public HashSet<OWLDataProperty>    dataProperties   = null;

    public Set<OWLEntity>              cl               = new HashSet<OWLEntity>();
    public Set<OWLEntity>              op               = new HashSet<OWLEntity>();
    public Set<OWLEntity>              dp               = new HashSet<OWLEntity>();

    public Map<OWLEntity, Set<String>> info;

    public StoredOntology(URI uri) throws CodiException
    {
        this.storeOntology(uri);
    }


    private void storeOntology(URI uri) throws CodiException
    {
        try {
            this.man = OWLManager.createOWLOntologyManager();
            this.ont = this.man.loadOntology(IRI.create(uri));
            this.factory = this.man.getOWLDataFactory();
            this.NOTHING = factory.getOWLNothing();
            this.THING = factory.getOWLThing();

            this.loadClasses();
            this.loadObjectProperties();
            this.loadDatatypeProperties();
            this.getInfo();
            reasoner = PelletReasonerFactory.getInstance().createReasoner(this.ont);
            reasoner.precomputeInferences();
        } catch(OWLOntologyCreationException e) {
            throw new CodiException("Ontology file at " + uri + " could not sucessfully be parsed", e);
        }
    }


    private void loadClasses()
    {
        HashSet<OWLClass> resultList = new HashSet<OWLClass>();
        Set<OWLClass> classes = this.ont.getClassesInSignature();
        for(OWLClass concept : classes) {
            if(!concept.equals(this.NOTHING) && !concept.equals(this.THING)) {
                resultList.add(concept);
            }
        }
        this.classes = resultList;
    }


    /*
     * Load all existing ObjectProperites without the Top and Bottom Property.
     */
    private void loadObjectProperties()
    {
        HashSet<OWLObjectProperty> resultList = new HashSet<OWLObjectProperty>();
        Set<OWLObjectProperty> objProps = this.ont.getObjectPropertiesInSignature();
        for(OWLObjectProperty prop : objProps) {
            if(!prop.getIRI().toString().startsWith(Namespaces.OWL.toString())) {
                resultList.add(prop);
            }
        }
        // System.out.println("number obs: "+ resultList.size());
        this.objectProperties = resultList;
    }


    /*
     * Load all existing DataProperites without the Top and Bottom Property.
     */
    private void loadDatatypeProperties()
    {
        HashSet<OWLDataProperty> resultList = new HashSet<OWLDataProperty>();
        Set<OWLDataProperty> dataProps = this.ont.getDataPropertiesInSignature();
        for(OWLDataProperty prop : dataProps) {
            if(!prop.getIRI().toString().startsWith(Namespaces.OWL.toString())) {
                resultList.add(prop);
            }
        }
        this.dataProperties = resultList;
    }


    private void getInfo()
    {
        info = new HashMap<OWLEntity, Set<String>>();
        HashMap<String, String> map = new HashMap<String, String>();
        Pattern result = Pattern.compile("http://([^#].*owl#[^ ].*)");
        Matcher m;
        int annotationCount = 0;
        for(OWLAxiom ax : ont.getAxioms()) {
            if(ax instanceof OWLAnnotationAssertionAxiom) {
                OWLAnnotationAssertionAxiom aax = (OWLAnnotationAssertionAxiom) ax;
                annotationCount++;
                OWLAnnotation a = aax.getAnnotation();
                m = result.matcher(aax.getSubject().toString());
                m.find();
                String key = null;
                try {
                    key = m.group(1);
                } catch(Exception e) {
                    continue;
                }

                if(a.getValue() instanceof OWLLiteral) {
                    OWLLiteral val = (OWLLiteral) a.getValue();
                    map.put(key, val.getLiteral());
                }
            }
        }

        Set<OWLEntity> entities = new HashSet<OWLEntity>();
        entities.addAll(ont.getClassesInSignature());
        entities.addAll(ont.getDataPropertiesInSignature());
        entities.addAll(ont.getObjectPropertiesInSignature());

        for(OWLClass cl1 : ont.getClassesInSignature()) {
            cl.add(cl1);
        }

        for(OWLDataProperty dp1 : ont.getDataPropertiesInSignature()) {
            dp.add(dp1);
        }
        for(OWLObjectProperty op1 : ont.getObjectPropertiesInSignature()) {
            op.add(op1);
        }

        double annationRate = (double) annotationCount / entities.size();
        boolean addAnnotation = false;
        if(annationRate > 0.1) {
            addAnnotation = true;
            System.out.println(ont + "\n\tannotations enabled (" + annationRate + ")");
        } else {
            System.out.println(ont + "\n\tannotations disabled (" + annationRate + ")");
        }

        for(OWLEntity ent : entities) {

            // if (ent instanceof OWLClass){
            // cl.add(ent);
            // }
            //
            // if (ent instanceof OWLDataProperty){
            // op.add(ent);
            // }
            //
            // if (ent instanceof OWLObjectProperty){
            // dp.add(ent);
            // }

            Set<String> pool = new HashSet<String>();
            String id = StringPreparation.standardize(ent.getIRI().getFragment());
            if(id.length() > 0) {
                pool.add(id);
            }
            if(addAnnotation) {
                Set<OWLAnnotation> annotations = ent.getAnnotations(ont);
                // System.out.println("\n" + ent + ": " + annotations.size());
                for(OWLAnnotation an : annotations) {
                    if(an.getValue() instanceof OWLLiteral) {
                        OWLLiteral val = (OWLLiteral) an.getValue();
                        // System.out.println("\t-> " + val.getLiteral());
                        String valString = val.getLiteral().trim();
                        valString = StringPreparation.standardize(valString);
                        if(valString.length() > 0 && valString.length() < 50)
                            pool.add(StringPreparation.standardize(val.getLiteral()));
                    } else {
                        try {
                            m = result.matcher(an.getValue().toString());
                            m.find();
                            String info = map.get(m.group(1));
                            info = StringPreparation.standardize(info);
                            // System.out.println("\t-> " + info);
                            if(info.length() <= 50 && info.length() > 0)
                                pool.add(info);
                        } catch(Exception e) {
                        }
                    }
                }
            }
            // System.out.println("\t-> " + pool.toString());
            pool = removeShortCut(pool);
            // if (pool.size() > 0){
            info.put(ent, pool);
            // }
        }

        // for (OWLEntity e: info.keySet()){
        // System.out.println(e);
        // for (String s : info.get(e))
        // System.out.println("\t" + s);
        // }
    }


    private Set<String> removeShortCut(Set<String> values)
    {
        List<String> tmp = new ArrayList<String>(values);

        for(int i = 0; i < values.size(); i++) {
            for(int j = 0; j < values.size(); j++) {
                if(i != j) {
                    List<String> tmp_r = StringPreparation.shortCut(tmp.get(i), tmp.get(j));
                    if(tmp_r.get(0).equals(tmp_r.get(1)) && tmp.get(i).length() < tmp.get(j).length()) {
                        values.remove(tmp.get(i));
                        continue;
                    }
                    if(tmp_r.get(0).equals(tmp_r.get(1)) && tmp.get(j).length() < tmp.get(i).length()) {
                        values.remove(tmp.get(j));
                        continue;
                    }

                    // part2:
                    String shortString[] = null, longString[] = null;
                    int shortID = -1;

                    if(tmp.get(i).length() != tmp.get(j).length()) {
                        if(tmp.get(i).length() < tmp.get(j).length()) {
                            shortString = tmp.get(i).split(" ");
                            longString = tmp.get(j).split(" ");
                            shortID = i;
                        } else {
                            shortString = tmp.get(j).split(" ");
                            longString = tmp.get(i).split(" ");
                            shortID = j;
                        }
                        if(Math.abs(shortString.length - longString.length) > 2)
                            continue;
                        Set<Integer> check = new HashSet<Integer>();
                        for(int k = 0; k < shortString.length; k++) {
                            if(shortString[k].length() > 1)
                                continue;
                            for(int l = 0; l < longString.length; l++) {
                                if(!check.contains(l) && (longString[l].startsWith(shortString[k]) || longString[l].equals(shortString[k]))) {
                                    check.add(l);
                                }
                            }
                        }
                        if(check.size() >= shortString.length) {
                            values.remove(tmp.get(shortID));
                        }
                    }
                }
            }
        }
        return values;
    }
}
